//============================================================================
/// \file   FloatingDragPreview.cpp
/// \author Uwe Kindler
/// \date   26.11.2019
/// \brief  Implementation of CFloatingDragPreview
//============================================================================

//============================================================================
//                                   INCLUDES
//============================================================================
#include <AutoHideDockContainer.h>
#include "FloatingDragPreview.h"
#include <iostream>

#include <QEvent>
#include <QApplication>
#include <QPainter>
#include <QKeyEvent>

#include "DockWidget.h"
#include "DockAreaWidget.h"
#include "DockManager.h"
#include "DockContainerWidget.h"
#include "DockOverlay.h"
#include "AutoHideDockContainer.h"
#include "ads_globals.h"

namespace ads
{

/** 
 * Private data class (pimpl)
 * 私有数据类（pimpl）
 */
struct FloatingDragPreviewPrivate
{
    CFloatingDragPreview *_this;
    QWidget* Content;
    CDockWidget::DockWidgetFeatures ContentFeatures;
    CDockAreaWidget* ContentSourceArea = nullptr;
    QPoint DragStartMousePosition;
    CDockManager* DockManager;
    CDockContainerWidget *DropContainer = nullptr;
    qreal WindowOpacity;
    bool Hidden = false;
    QPixmap ContentPreviewPixmap;
    bool Canceled = false;

    /** 
     * Private data constructor
     * 私有数据构造函数
     */
    FloatingDragPreviewPrivate(CFloatingDragPreview *_public);

    /**
     * Update drop overlays at the given global position
     * 在给定的全局位置更新拖放覆盖层
     */
    void updateDropOverlays(const QPoint &GlobalPos);

    /**
     * Set the hidden state of the floating drag preview
     * 设置浮动拖放预览的隐藏状态
     */
    void setHidden(bool Value)
    {
        Hidden = Value;
        _this->update();
    }

    /**
     * Cancel dragging and emit the draggingCanceled event
     * 取消拖放并触发draggingCanceled事件
     */
    void cancelDragging()
    {
        Canceled = true;
        Q_EMIT _this->draggingCanceled();
        DockManager->containerOverlay()->hideOverlay();
        DockManager->dockAreaOverlay()->hideOverlay();
        _this->close();
    }

    /**
     * Creates the real floating widget in case the mouse is released outside of any drop area
     * 在鼠标释放到任何拖放区域之外时创建真正的浮动窗口部件
     */
    void createFloatingWidget();

    /**
     * Returns true if the content is floatable
     * 如果内容可浮动，则返回true
     */
    bool isContentFloatable() const
    {
        return this->ContentFeatures.testFlag(CDockWidget::DockWidgetFloatable);
    }

    /**
     * Returns true if the content is pinnable
     * 如果内容可固定，则返回true
     */
    bool isContentPinnable() const
    {
        return this->ContentFeatures.testFlag(CDockWidget::DockWidgetPinnable);
    }

    /**
     * Returns the content features
     * 返回内容的特性
     */
    CDockWidget::DockWidgetFeatures contentFeatures() const
    {
        CDockWidget* DockWidget = qobject_cast<CDockWidget*>(Content);
        if (DockWidget)
        {
            return DockWidget->features();
        }
        CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(Content);
        if (DockArea)
        {
            return DockArea->features();
        }
        return CDockWidget::DockWidgetFeatures();
    }
};

// struct LedArrayPanelPrivate


//============================================================================

void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &GlobalPos)
{
    // 检查是否可见以及DockManager是否存在
    if (!_this->isVisible() || !DockManager)
    {
        return;
    }

    // 获取所有的dock容器
    auto Containers = DockManager->dockContainers();

    // 初始化顶部容器
    CDockContainerWidget *TopContainer = nullptr;

    // 遍历所有容器
    for (auto ContainerWidget : Containers)
    {
        // 如果容器不可见，则跳过
        if (!ContainerWidget->isVisible())
        {
            continue;
        }

        // 将全局坐标映射到容器坐标系中
        QPoint MappedPos = ContainerWidget->mapFromGlobal(GlobalPos);

        // 检查鼠标位置是否在容器内
        if (ContainerWidget->rect().contains(MappedPos))
        {
            // 如果当前容器在顶部容器之前，则更新顶部容器为当前容器
            if (!TopContainer || ContainerWidget->isInFrontOf(TopContainer))
            {
                TopContainer = ContainerWidget;
            }
        }
    }

    // 更新DropContainer
    DropContainer = TopContainer;

    // 获取ContainerOverlay和DockAreaOverlay
    auto ContainerOverlay = DockManager->containerOverlay();
    auto DockAreaOverlay = DockManager->dockAreaOverlay();

    // 如果没有顶部容器，则隐藏ContainerOverlay和DockAreaOverlay
    if (!TopContainer)
    {
        ContainerOverlay->hideOverlay();
        DockAreaOverlay->hideOverlay();

        // 如果DragPreviewIsDynamic标志被设置，则显示DragPreview
        if (CDockManager::testConfigFlag(CDockManager::DragPreviewIsDynamic))
        {
            setHidden(false);
        }

        return;
    }

    // 获取DockDropArea和ContainerDropArea
    auto DockDropArea = DockAreaOverlay->dropAreaUnderCursor();
    auto ContainerDropArea = ContainerOverlay->dropAreaUnderCursor();

    // 获取可见的DockAreas数量
    int VisibleDockAreas = TopContainer->visibleDockAreaCount();

    // 如果Content是DockAreaWidget并且是自动隐藏的，则可见的DockAreas数量加1
    auto dockAreaWidget = qobject_cast<CDockAreaWidget*>(Content);
    if (dockAreaWidget && dockAreaWidget->isAutoHide())
    {
        VisibleDockAreas++;
    }

    // 根据可见的DockAreas数量设置AllowedContainerAreas
    DockWidgetAreas AllowedContainerAreas = (VisibleDockAreas > 1) ? OuterDockAreas : AllDockAreas;

    // 如果Content是可固定的，则AllowedContainerAreas包括AutoHideDockAreas
    if (isContentPinnable())
    {
        AllowedContainerAreas |= AutoHideDockAreas;
    }

    // 设置ContainerOverlay的AllowedAreas
    ContainerOverlay->setAllowedAreas(AllowedContainerAreas);

    // 根据ContainerDropArea是否为InvalidDockWidgetArea设置ContainerOverlay的DropPreview
    ContainerOverlay->enableDropPreview(ContainerDropArea != InvalidDockWidgetArea);

    // 获取DockArea
    auto DockArea = TopContainer->dockAreaAt(GlobalPos);

    // 如果DockArea只包含一个DockArea，则根据allowedAreas设置AllowedContainerAreas
    if (VisibleDockAreas == 1 && DockArea)
    {
        AllowedContainerAreas.setFlag(CenterDockWidgetArea, DockArea->allowedAreas().testFlag(CenterDockWidgetArea));
    }

    // 设置ContainerOverlay的AllowedAreas
    ContainerOverlay->setAllowedAreas(AllowedContainerAreas);

    // 根据DockArea是否可见以及VisibleDockAreas是否大于等于0设置DockAreaOverlay的DropPreview
    if (DockArea && DockArea->isVisible() && VisibleDockAreas >= 0 && DockArea != ContentSourceArea)
    {
        DockAreaOverlay->enableDropPreview(true);
        DockAreaOverlay->setAllowedAreas((VisibleDockAreas == 1) ? NoDockWidgetArea : DockArea->allowedAreas());

        // 显示DockAreaOverlay，并获取Area
        DockWidgetArea Area = DockAreaOverlay->showOverlay(DockArea);

        // 如果Area为CenterDockWidgetArea并且ContainerDropArea不为InvalidDockWidgetArea，则禁用DockAreaOverlay的DropPreview，启用ContainerOverlay的DropPreview
        if ((Area == CenterDockWidgetArea) && (ContainerDropArea != InvalidDockWidgetArea))
        {
            DockAreaOverlay->enableDropPreview(false);
            ContainerOverlay->enableDropPreview(true);
        }
        else
        {
            ContainerOverlay->enableDropPreview(InvalidDockWidgetArea == Area);
        }

        // 显示ContainerOverlay
        ContainerOverlay->showOverlay(TopContainer);
    }
    else
    {
        // 隐藏DockAreaOverlay

        DockAreaOverlay->hideOverlay();

        // 如果只有一个可见的DockArea，则设置ContainerOverlay的AllowedAreas为AutoHideDockAreas
        if (VisibleDockAreas == 1)
        {
            ContainerOverlay->setAllowedAreas(AutoHideDockAreas);
        }

        // 显示ContainerOverlay
        ContainerOverlay->showOverlay(TopContainer);

        // 如果DockArea为ContentSourceArea并且ContainerDropArea为InvalidDockWidgetArea，则将DropContainer设置为nullptr
        if (DockArea == ContentSourceArea && InvalidDockWidgetArea == ContainerDropArea)
        {
            DropContainer = nullptr;
        }
    }

    // 如果DragPreviewIsDynamic标志被设置，则根据DockDropArea和ContainerDropArea是否为InvalidDockWidgetArea设置隐藏状态
    if (CDockManager::testConfigFlag(CDockManager::DragPreviewIsDynamic))
    {
        setHidden(DockDropArea != InvalidDockWidgetArea || ContainerDropArea != InvalidDockWidgetArea);
    }
}


//============================================================================
// FloatingDragPreviewPrivate类
//============================================================================

// 构造函数
FloatingDragPreviewPrivate::FloatingDragPreviewPrivate(CFloatingDragPreview *_public) :
    _this(_public)
{
}

// 创建浮动窗口部件
void FloatingDragPreviewPrivate::createFloatingWidget()
{
    CDockWidget* dockWidget = qobject_cast<CDockWidget*>(Content);
    CDockAreaWidget* dockArea = qobject_cast<CDockAreaWidget*>(Content);
    CFloatingDockContainer* floatingWidget = nullptr;

    // 检查内容是否为DockWidget并且可浮动
    if (dockWidget && dockWidget->features().testFlag(CDockWidget::DockWidgetFloatable))
    {
        floatingWidget = new CFloatingDockContainer(dockWidget);
    }
    // 检查内容是否为DockArea并且可浮动
    else if (dockArea && dockArea->features().testFlag(CDockWidget::DockWidgetFloatable))
    {
        floatingWidget = new CFloatingDockContainer(dockArea);
    }

    // 如果浮动窗口部件创建成功
    if (floatingWidget)
    {
        floatingWidget->setGeometry(_this->geometry());
        floatingWidget->show();

        // 如果拖放预览没有窗口边框，则调整几何形状
        if (!CDockManager::testConfigFlag(CDockManager::DragPreviewHasWindowFrame))
        {
            QApplication::processEvents();
            int frameHeight = floatingWidget->frameGeometry().height() - floatingWidget->geometry().height();
            QRect fixedGeometry = _this->geometry();
            fixedGeometry.adjust(0, frameHeight, 0, 0);
            floatingWidget->setGeometry(fixedGeometry);
        }
    }
}

//============================================================================
// CFloatingDragPreview类
//============================================================================

// 构造函数
CFloatingDragPreview::CFloatingDragPreview(QWidget* Content, QWidget* parent) :
    QWidget(parent),
    d(new FloatingDragPreviewPrivate(this))
{
    d->Content = Content;
    d->ContentFeatures = d->contentFeatures();
    setAttribute(Qt::WA_DeleteOnClose);

    // 根据配置设置窗口标志
    if (CDockManager::testConfigFlag(CDockManager::DragPreviewHasWindowFrame))
    {
        setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
    }
    else
    {
        setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
        setAttribute(Qt::WA_NoSystemBackground);
        setAttribute(Qt::WA_TranslucentBackground);
    }

    // UNIX系统的窗口标志
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
    auto flags = windowFlags();
    flags |= Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint;
    setWindowFlags(flags);
#endif

    // 创建内容的静态图像
    if (CDockManager::testConfigFlag(CDockManager::DragPreviewShowsContentPixmap))
    {
        d->ContentPreviewPixmap = QPixmap(Content->size());
        Content->render(&d->ContentPreviewPixmap);
    }

    // 连接信号并安装事件过滤器
    connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), SLOT(onApplicationStateChanged(Qt::ApplicationState)));
    qApp->installEventFilter(this);
}

// DockWidget内容的构造函数
CFloatingDragPreview::CFloatingDragPreview(CDockWidget* Content) :
    CFloatingDragPreview((QWidget*)Content, Content->dockManager())
{
    d->DockManager = Content->dockManager();

    // 如果内容的DockAreaWidget只有一个打开的DockWidget，则设置ContentSourceArea
    if (Content->dockAreaWidget()->openDockWidgetsCount() == 1)
    {
        d->ContentSourceArea = Content->dockAreaWidget();
    }

    setWindowTitle(Content->windowTitle());
}

// DockArea内容的构造函数
CFloatingDragPreview::CFloatingDragPreview(CDockAreaWidget* Content) :
    CFloatingDragPreview((QWidget*)Content, Content->dockManager())
{
    d->DockManager = Content->dockManager();
    d->ContentSourceArea = Content;
    setWindowTitle(Content->currentDockWidget()->windowTitle());
}

// 析构函数
CFloatingDragPreview::~CFloatingDragPreview()
{
    delete d;
}


//============================================================================
void CFloatingDragPreview::moveFloating()
{
    // 计算边框大小
    int BorderSize = (frameSize().width() - size().width()) / 2;
    // 计算新的位置以移动窗口部件
    const QPoint moveToPos = QCursor::pos() - d->DragStartMousePosition - QPoint(BorderSize, 0);
    // 移动窗口部件到新的位置
    move(moveToPos);
    // 更新拖放覆盖层
    d->updateDropOverlays(QCursor::pos());
}

//============================================================================
void CFloatingDragPreview::startFloating(const QPoint &DragStartMousePos, const QSize &Size, eDragState DragState, QWidget *MouseEventHandler)
{
    // 未使用的参数
    Q_UNUSED(MouseEventHandler)
    Q_UNUSED(DragState)
    // 调整窗口部件的大小
    resize(Size);
    // 设置拖放开始时的鼠标位置
    d->DragStartMousePosition = DragStartMousePos;
    // 移动窗口部件
    moveFloating();
    // 显示窗口部件
    show();
}

//============================================================================
void CFloatingDragPreview::finishDragging()
{
    ADS_PRINT("CFloatingDragPreview::finishDragging");
    // 获取鼠标光标下的可见拖放区域
    auto DockDropArea = d->DockManager->dockAreaOverlay()->visibleDropAreaUnderCursor();
    auto ContainerDropArea = d->DockManager->containerOverlay()->visibleDropAreaUnderCursor();
    // 检查是否存在有效的拖放区域
    bool ValidDropArea = (DockDropArea != InvalidDockWidgetArea) || (ContainerDropArea != InvalidDockWidgetArea);
    // 如果内容不可浮动，则非浮动自动隐藏部件应保持其当前自动隐藏状态
    if (ValidDropArea || d->isContentFloatable())
    {
        cleanupAutoHideContainerWidget(ContainerDropArea);
    }
    if (!d->DropContainer)
    {
        d->createFloatingWidget();
    }
    else if (DockDropArea != InvalidDockWidgetArea)
    {
        d->DropContainer->dropWidget(d->Content, DockDropArea, d->DropContainer->dockAreaAt(QCursor::pos()), d->DockManager->dockAreaOverlay()->tabIndexUnderCursor());
    }
    else if (ContainerDropArea != InvalidDockWidgetArea)
    {
        CDockAreaWidget* DockArea = nullptr;
        // 如果只有一个可见的停靠区域，并且拖放到中心区域，则将拖放的部件添加到唯一可见的停靠区域中
        if (d->DropContainer->visibleDockAreaCount() <= 1 && CenterDockWidgetArea == ContainerDropArea)
        {
            DockArea = d->DropContainer->dockAreaAt(QCursor::pos());
        }
        d->DropContainer->dropWidget(d->Content, ContainerDropArea, DockArea, d->DockManager->containerOverlay()->tabIndexUnderCursor());
    }
    else
    {
        d->createFloatingWidget();
    }
    this->close();
    d->DockManager->containerOverlay()->hideOverlay();
    d->DockManager->dockAreaOverlay()->hideOverlay();
}

//============================================================================
void CFloatingDragPreview::cleanupAutoHideContainerWidget(DockWidgetArea ContainerDropArea)
{
    auto DroppedDockWidget = qobject_cast<CDockWidget*>(d->Content);
    auto DroppedArea = qobject_cast<CDockAreaWidget*>(d->Content);
    auto AutoHideContainer = DroppedDockWidget ? DroppedDockWidget->autoHideDockContainer() : DroppedArea->autoHideDockContainer();
    if (!AutoHideContainer)
    {
        return;
    }
    // 如果拖放的部件已经是自动隐藏部件，并且如果将其移动到同一容器中的新侧边栏位置，则无需进行清理
    if (ads::internal::isSideBarArea(ContainerDropArea) && (d->DropContainer == AutoHideContainer->dockContainer()))
    {
        return;
    }
    AutoHideContainer->cleanupAndDelete();
}


//============================================================================
void CFloatingDragPreview::paintEvent(QPaintEvent* event)
{
    Q_UNUSED(event);

    // 如果预览被隐藏，则返回
    if (d->Hidden)
    {
        return;
    }

    QPainter painter(this);
    painter.setOpacity(0.6);

    // 如果设置了DragPreviewShowsContentPixmap标志，则绘制内容预览图像
    if (CDockManager::testConfigFlag(CDockManager::DragPreviewShowsContentPixmap))
    {
        painter.drawPixmap(QPoint(0, 0), d->ContentPreviewPixmap);
    }

    // 如果未设置DragPreviewHasWindowFrame标志，则绘制无边框窗口
    if (!CDockManager::testConfigFlag(CDockManager::DragPreviewHasWindowFrame))
    {
        QColor Color = palette().color(QPalette::Active, QPalette::Highlight);
        QPen Pen = painter.pen();
        Pen.setColor(Color.darker(120));
        Pen.setStyle(Qt::SolidLine);
        Pen.setWidth(1);
        Pen.setCosmetic(true);
        painter.setPen(Pen);
        Color = Color.lighter(130);
        Color.setAlpha(64);
        painter.setBrush(Color);
        painter.drawRect(rect().adjusted(0, 0, -1, -1));
    }
}

//============================================================================
void CFloatingDragPreview::onApplicationStateChanged(Qt::ApplicationState state)
{
    // 如果应用程序状态不是活动状态，则取消拖放并断开信号连接
    if (state != Qt::ApplicationActive)
    {
        disconnect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
            this, SLOT(onApplicationStateChanged(Qt::ApplicationState)));
        d->cancelDragging();
    }
}

//============================================================================
bool CFloatingDragPreview::eventFilter(QObject *watched, QEvent *event)
{
    Q_UNUSED(watched);
    if (!d->Canceled && event->type() == QEvent::KeyPress)
    {
        QKeyEvent* e = static_cast<QKeyEvent*>(event);
        if (e->key() == Qt::Key_Escape)
        {
            watched->removeEventFilter(this);
            d->cancelDragging();
        }
    }

    return false;
}



} // namespace ads

//---------------------------------------------------------------------------
// EOF FloatingDragPreview.cpp
